// reflection_demo.go
package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Name string `json:"name"`
	age  int    // 不可导出字段
}

func main() {
	// 1. 基本类型
	var x float64 = 3.14
	v := reflect.ValueOf(x)
	t := reflect.TypeOf(x)
	fmt.Println("【基本类型】")
	fmt.Printf("  value=%v  type=%v  kind=%v\n\n", v, t, t.Kind())

	// 2. 指针
	px := &x
	pv := reflect.ValueOf(px)
	pt := reflect.TypeOf(px)
	fmt.Println("【指针】")
	fmt.Printf("  value=%v  type=%v  kind=%v  elem=%v\n\n",
		pv, pt, pt.Kind(), pv.Elem())

	// 3. 结构体
	u := User{Name: "Alice", age: 18}
	ut := reflect.TypeOf(u)
	fmt.Println("【结构体】")
	fmt.Printf("  type=%v  kind=%v  fields=%d\n", ut, ut.Kind(), ut.NumField())
	for i := 0; i < ut.NumField(); i++ {
		f := ut.Field(i)
		fmt.Printf("    field[%d] name=%s  type=%v  tag=%v  exported=%t\n",
			i, f.Name, f.Type, f.Tag, f.IsExported())
	}

	// 4. 修改可寻址的值（通过指针）
	puv := reflect.ValueOf(&u).Elem() // 必须拿到指针的 Elem 才可设置
	if name := puv.FieldByName("Name"); name.IsValid() && name.CanSet() {
		name.SetString("Bob")
	}
	if age := puv.FieldByName("age"); age.IsValid() && age.CanSet() {
		age.SetInt(20)
	} else {
		fmt.Println("  age 字段不可导出，无法直接设置")
	}
	fmt.Printf("  修改后 u=%+v\n\n", u)

	// 5. 切片
	s := []int{1, 2, 3}
	sv := reflect.ValueOf(s)
	st := reflect.TypeOf(s)
	fmt.Println("【切片】")
	fmt.Printf("  type=%v  kind=%v  len=%d  cap=%d\n", st, st.Kind(), sv.Len(), sv.Cap())
	for i := 0; i < sv.Len(); i++ {
		fmt.Printf("    [%d]=%d ", i, sv.Index(i).Int())
	}
	fmt.Println("\n")

	// 6. 通过反射创建新值（零值）
	newInt := reflect.New(reflect.TypeOf(42)).Elem()
	newInt.SetInt(99)
	fmt.Println("【通过反射创建并设置值】")
	fmt.Printf("  newInt=%v  type=%v\n", newInt.Int(), newInt.Type())
}

// 反射的基本概念
// Go语言提供了一种机制在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作，但是在编译时并不知道这些变量的具体类型，这种机制被称为反射。
// 反射也可以让我们将类型本身作为第一类的值类型处理。

// 使用反射获取结构体的成员类型
// Field(i int) StructField	根据索引返回索引对应的结构体字段的信息，当值不是结构体或索引超界时发生宕机
// NumField() int	返回结构体成员字段数量，当类型不是结构体或索引超界时发生宕机
// FieldByName(name string) (StructField, bool)	根据给定字符串返回字符串对应的结构体字段的信息，没有找到时 bool 返回 false，当类型不是结构体或索引超界时发生宕机
// FieldByIndex(index []int) StructField	多层成员访问时，根据 []int 提供的每个结构体的字段索引，返回字段的信息，没有找到时返回零值。当类型不是结构体或索引超界时发生宕机
// FieldByNameFunc(match func(string) bool) (StructField,bool)	根据匹配函数匹配需要的字段，当值不是结构体或索引超界时发生宕机

// 结构体字段类型
// reflect.Type 的 Field() 方法返回 StructField 结构，
// 这个结构描述结构体的成员信息，通过这个信息可以获取成员与结构体的关系，如偏移、索引、是否为匿名字段、结构体标签（StructTag）等，
// 而且还可以通过 StructField 的 Type 字段进一步获取结构体成员的类型信息。
// StructField 的结构如下：
// type StructField struct {
//     Name string          // 字段名
//     PkgPath string       // 字段路径
//     Type      Type       // 字段反射类型对象
//     Tag       StructTag  // 字段的结构体标签
//     Offset    uintptr    // 字段在结构体中的相对偏移
//     Index     []int      // Type.FieldByIndex中的返回的索引值
//     Anonymous bool       // 是否为匿名字段
// }
